/* Copyright 2009 Requirement Management System
 * 
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.mags.dao.provider;

import java.io.Serializable; 
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.apache.commons.beanutils.PropertyUtils;
import org.mags.dao.common.CacheableDataProvider;
import org.mags.dao.common.EntityProperties;
import org.mags.dao.common.Identifiable;
import org.mags.dao.common.Limits;
import org.mags.dao.common.SOrder;
import org.mags.dao.common.RestrictionAttribute;
import org.mags.dao.common.RestrictionOperator;

/**
 * 
 * @author Mario Gaitan
 *
 */
public abstract class AbstractCacheableDataProvider<T> 
		extends AbstractDataProvider<T> 
		implements CacheableDataProvider<T>{
	private Collection<T> all = null;
	private SOrder[] defaultOrderBy = null;
	private String[] defaultAssociationPaths = null;
	private Boolean isIdentifiable = null;
	private Comparator<T> comparator;
	
	protected String[] getDefaultAssociationPaths() {
		return defaultAssociationPaths;
	}
	protected void setDefaultAssociationPaths(String[] defaultAssociationPaths) {
		this.defaultAssociationPaths = defaultAssociationPaths;
	}
	protected Comparator<T> getComparator() {
		return comparator;
	}
	protected void setComparator(Comparator<T> comparator) {
		this.comparator = comparator;
	}
	protected SOrder[] getDefaultOrderBy() {
		return defaultOrderBy;
	}
	protected void setDefaultOrderBy(SOrder[] defaultOrderBy) {
		this.defaultOrderBy = defaultOrderBy;
	}
	@Override
	public void refreshAll() {
		all = null;
		loadAll();
	}
	@SuppressWarnings("unchecked")
	private synchronized void loadAll() {		 
		if(all==null) {
			EntityProperties entityProperties = new EntityProperties(defaultAssociationPaths);
			all = super.getAll(entityProperties, defaultOrderBy);
			loadProperties(all);
			if(comparator!=null) {
				List<T> list = null;
				if(all instanceof List)
					list = (List<T>)all;
				else {
					T[] array = (T[])Array.newInstance(getPersistentClass(), 0);
					list = Arrays.asList(all.toArray(array));
				}
				Collections.sort(list, comparator);
				all = list;
			}
		}
	}
	protected void loadProperties(Collection<T> all) {
		for(T i: all)
			loadProperties(i);
	}
	@Override
	public void createOrUpdate(T record) {
		super.createOrUpdate(record);
		all = null;
		loadAll();
	}
	@Override
	public Serializable create(T record) {
		Serializable id = super.create(record);
		all = null;
		loadAll();		
		return id;
	}
	@Override
	public void update(T record) {
		super.update(record);
		all = null;
		loadAll();
	}	
	@Override
	public void delete(T record) {
		super.delete(record);
		all = null;
		loadAll();		
	}
	@Override
	public Collection<T> getAll() {
		if(all==null) 
			loadAll();
		return all;
	}
	@Override
	public Number count() {
		Collection<T> all = getAll();
		return all.size();
	}	
	@Override
	public boolean existsWithId(Serializable id) {
		if(getById(id)==null)
			return false;
		else
			return true;
	}
	private synchronized void checkIfIdentifiable() {
		Object o = super.newInstance();
		if(o instanceof Identifiable)
			isIdentifiable = true;
	}
	protected Collection<T> getByAttribute(RestrictionAttribute attribute, SOrder[] orderBy, Limits limits) {
		throw new RuntimeException("cacheable data provider don't support this function");
	}	
	@Override
	public T getOneByAttribute(RestrictionAttribute attribute) {
		if(attribute.getOperator()!=RestrictionOperator.EQ)
			throw new RuntimeException("just = operator is supported");
		try {
			for(T i: getAll()) {
				Object property = PropertyUtils.getProperty(i, attribute.getName());
				if(property!=null&&property.equals(attribute.getValue()))
					return i;
			}
		} catch(Exception e) {
			if(e instanceof RuntimeException)
				throw (RuntimeException)e;
			else
				throw new RuntimeException(e);
		}
		return null;
	}
	@Override
	public T getById(Serializable id) {
		if(isIdentifiable==null)
			checkIfIdentifiable();
		if(isIdentifiable) {
			for(T i: getAll()) {
				if(((Identifiable)i).getId().equals(id))
					return i;
			}
		}
		return null;
	}
	/**
	 * implements when need load properties to objects that this is getting
	 * @param one: object to load properties
	 */	
	protected void loadProperties(T one) {
		//DO NOTHING
	}
}
